/* =============================== 通用拖拽处理类 =============================== */
import { AppStyleCollections, logger } from '../types/constants'
import { SYMBOLS } from '../types/interfaces'
import { ToolKit } from './utils/toolkit'

export interface DragElementConfig {
  containerSelector: string
  dragTargetSelector: string
  dragTargetStyle?: string
  handleIconStyle?: string
  position?: 'left' | 'right'
}

export interface DragBehaviorConfig {
  onPrepare?: () => boolean
  storageKey: string
  positionCallback?: (right: number, top: number) => void
}

export interface DragHandlerParams extends DragBehaviorConfig {
  elementConfig: DragElementConfig
}

export interface DragConfig extends DragElementConfig, DragBehaviorConfig {}

export class DragHandler {
  private readonly name: string = 'DragHandler'
  private readonly config: DragConfig
  private readonly selector: string

  constructor(config: DragHandlerParams) {
    const { elementConfig, ...restConfig } = config

    this.config = {
      ...restConfig,
      ...elementConfig,
    }

    this.selector = `${this.config.containerSelector} ${this.config.dragTargetSelector}`

    logger.attention(`[${this.name}] 侧边栏拖动配置`, this.config)
  }

  init(): void {
    this.createDragElement()
    this.setup()
  }

  /**
   * 创建拖拽元素
   */
  createDragElement(): void {
    const { onPrepare, containerSelector, dragTargetStyle, handleIconStyle = '', position = 'left' } = this.config

    const success = onPrepare?.() || true

    if (!success)
      return

    const $container = $(containerSelector)

    if (!$container || $container.length === 0) {
      logger.error(`[${this.name}] 拖拽主容器不存在: ${containerSelector}`)
      return
    }

    if ($container.length > 1) {
      logger.warn(`[${this.name}] 拖拽主容器存在多个: ${containerSelector}`)
      return
    }

    $container.addClass(AppStyleCollections.Drag.WRAPPER)

    const $element = $(this.selector)

    if (!$element || $element.length === 0) {
      logger.error(`[${this.name}] 拖拽容器不存在: ${this.selector}`)
      return
    }

    if ($element.length > 1) {
      logger.warn(`[${this.name}] 拖拽容器存在多个: ${this.selector}`)
      return
    }

    if ($element.hasClass(AppStyleCollections.Drag.CONTAINER)) {
      logger.info(`[${this.name}] ${SYMBOLS.STATUS.SUCCESS} 拖拽容器已存在: ${this.selector}`)
      return
    }

    $element
      .addClass(AppStyleCollections.Drag.CONTAINER)
      .append(`<span class="${AppStyleCollections.Drag.ICON}" style=${handleIconStyle} data-position=${position}></span>`)

    ToolKit.dom.addStyle($element, dragTargetStyle || '')

    logger.info(`[${this.name}] ${SYMBOLS.STATUS.SUCCESS} 拖拽图标添加成功`)
  }

  /**
   * 初始化拖拽事件
   */
  private setup(): void {
    let isDragging = false
    let startX = 0
    let startY = 0
    let startRight = 0
    let startTop = 0

    $(document)
      .on('mousedown', `${this.config.containerSelector} .${AppStyleCollections.Drag.CONTAINER}`, (e) => {
        isDragging = true
        this.toggleSelectionClass(true)

        const $containerParent = $(e.currentTarget).parent()
        const rect = $containerParent[0].getBoundingClientRect()

        startX = e.clientX
        startY = e.clientY
        startRight = window.innerWidth - rect.right
        startTop = rect.top

        $containerParent.css({ 'transition': 'none', 'user-select': 'none' })
      })
      .on('mousemove', (e) => {
        if (!isDragging) {
          this.toggleSelectionClass(false)
          return
        }

        const deltaX = e.clientX - startX
        const deltaY = e.clientY - startY
        const $container = $(this.config.containerSelector)
        const winWidth = window.innerWidth
        const winHeight = window.innerHeight
        const menuWidth = $container[0].offsetWidth
        const menuHeight = $container[0].offsetHeight

        const newRight = Math.max(0, Math.min(startRight - deltaX, winWidth - menuWidth))
        const newTop = Math.max(0, Math.min(startTop + deltaY, winHeight - menuHeight))

        $container[0]?.style.setProperty('right', `${newRight}px`, 'important')
        $container[0]?.style.setProperty('top', `${newTop}px`, 'important')
        $container[0]?.style.setProperty('user-select', 'none', 'important')

        this.config.positionCallback?.(newRight, newTop)
      })
      .on('mouseup', () => {
        if (!isDragging)
          return

        isDragging = false
        this.toggleSelectionClass(false)

        const $container = $(this.config.containerSelector)
        const finalRight = Number.parseFloat($container.css('right'))
        const finalTop = Number.parseFloat($container.css('top'))

        localStorage.setItem(this.config.storageKey, JSON.stringify({
          right: Math.max(0, finalRight),
          top: Math.max(0, finalTop),
        }))
        $container.css({ 'transition': '', 'user-select': '' })
      })

    this.toggleSelectionClass(false)
    this.applyPositionCache()
  }

  /**
   * 添加文本选择 CSS 样式类
   */
  private toggleSelectionClass(prevent: boolean): void {
    $(document.body).toggleClass(AppStyleCollections.Drag.NO_SELECTION, prevent)
  }

  /**
   * 验证并获取有效位置
   */
  private getValidatedPosition(right: number, top: number): {
    right: number
    top: number
  } {
    const $container = $(this.config.containerSelector)

    if (!$container.length) {
      logger.info(`[${this.name}] 未找到拖拽主容器：${this.config.containerSelector}`)
      return { right: 0, top: 0 }
    }

    const winWidth = window.innerWidth
    const winHeight = window.innerHeight
    const menuWidth = $container[0]?.offsetWidth || 300
    const menuHeight = $container[0]?.offsetHeight || 200

    return {
      right: Math.max(0, Math.min(right, winWidth - menuWidth)),
      top: Math.max(0, Math.min(top, winHeight - menuHeight)),
    }
  }

  /**
   * 应用位置缓存
   */
  private applyPositionCache() {
    const cache = localStorage.getItem(this.config.storageKey)
    if (!cache)
      return

    requestAnimationFrame(() => {
      const $container = $(this.config.containerSelector)
      $container.css({
        opacity: '0',
        transition: 'none',
      })

      requestAnimationFrame(() => {
        try {
          const { right, top } = JSON.parse(cache)
          const validatedPos = this.getValidatedPosition(right, top)
          $container.css({
            right: `${validatedPos.right}px`,
            top: `${validatedPos.top}px`,
            opacity: '1',
            transition: 'right 0.3s ease-out, top 0.3s ease-out, opacity 0.2s ease-out',
          })
        }
        catch (e) {
          console.error(`[${this.name}] 位置缓存解析失败`, e)
        }
      })
    })
  }
}
